{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "github地址:[https://github.com/cheesezh/python_design_patterns](https://github.com/cheesezh/python_design_patterns)\n",
    "\n",
    "## 题目1\n",
    "用程序模拟一个画小人的过程，要求小人要有头，身子，左手，右手，左脚，右脚。\n",
    "\n",
    "## 基础代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "画头\n",
      "画身子\n",
      "画左手\n",
      "画右手\n",
      "画左脚\n",
      "画右脚\n"
     ]
    }
   ],
   "source": [
    "class PersonDrawer():\n",
    "    \n",
    "    def draw(self):\n",
    "        print(\"画头\")\n",
    "        print(\"画身子\")\n",
    "        print(\"画左手\")\n",
    "        print(\"画右手\")\n",
    "        print(\"画左脚\")\n",
    "        print(\"画右脚\")\n",
    "        \n",
    "def main():\n",
    "    drawer = PersonDrawer()\n",
    "    drawer.draw()\n",
    "    \n",
    "main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 题目2\n",
    "再画一个身子胖一些的小人。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "画头\n",
      "画身子(胖一些)\n",
      "画左手\n",
      "画右手\n",
      "画右脚\n"
     ]
    }
   ],
   "source": [
    "class FatPersonDrawer():\n",
    "    \n",
    "    def draw(self):\n",
    "        print(\"画头\")\n",
    "        print(\"画身子(胖一些)\")\n",
    "        print(\"画左手\")\n",
    "        print(\"画右手\")\n",
    "        print(\"画右脚\")\n",
    "        \n",
    "def main():\n",
    "    drawer = FatPersonDrawer()\n",
    "    drawer.draw()\n",
    "    \n",
    "main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 点评\n",
    "画胖一些的小人时候，稍不注意，漏掉了左脚。画小人的步骤很繁琐，稍不注意就会漏掉一步，导致小人缺胳膊少腿。最好的办法就是规定，建造小人的时候，必须要有头，身子，左手，右手，左脚，右脚。这时候就可以使用建造这模式。\n",
    "\n",
    "## 建造者模式\n",
    "\n",
    "建造小人的`过程`是稳定的，都需要头身手脚，但是具体建造的`细节`是不同的，高矮胖瘦都有。\n",
    "\n",
    "为了将一个复杂的对象的`构建`与它的`表示`分离，使得`同样的构建`过程可以创建`不同的表示`，就可以使用建造者模式，又叫“生成器模式”。\n",
    "\n",
    "实现的方法就是定义抽象的建造者类，通过抽象函数的方法把构建的过程固定下来，这样就每一步也不能少，不会出现缺胳膊少腿的情况了。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "画头——瘦小人\n",
      "画身子——瘦小人\n",
      "画左手——瘦小人\n",
      "画右手——瘦小人\n",
      "画左腿——瘦小人\n",
      "画头——胖小人\n",
      "画身子——胖小人\n",
      "画左手——胖小人\n",
      "画右手——胖小人\n",
      "画左腿——胖小人\n"
     ]
    }
   ],
   "source": [
    "from abc import ABCMeta, abstractmethod\n",
    "\n",
    "\n",
    "class PersonBuilder():\n",
    "    \"\"\"\n",
    "    抽象建造者，定义创建对象各个部件的接口\n",
    "    \"\"\"\n",
    "    __metaclass__ = ABCMeta\n",
    "    \n",
    "    @abstractmethod\n",
    "    def draw_head(self):\n",
    "        pass\n",
    "    \n",
    "    @abstractmethod\n",
    "    def draw_body(self):\n",
    "        pass\n",
    "    \n",
    "    @abstractmethod\n",
    "    def draw_left_arm(self):\n",
    "        pass\n",
    "    \n",
    "    @abstractmethod\n",
    "    def draw_right_arm(self):\n",
    "        pass\n",
    "    \n",
    "    @abstractmethod\n",
    "    def draw_left_leg(self):\n",
    "        pass\n",
    "    \n",
    "    @abstractmethod\n",
    "    def draw_right_leg(self):\n",
    "        pass\n",
    "\n",
    "    \n",
    "class ThinPersonBuilder(PersonBuilder):\n",
    "    \"\"\"\n",
    "    具体建造者，实现创建各个部件的接口\n",
    "    \"\"\"\n",
    "    def draw_head(self):\n",
    "        print(\"画头——瘦小人\")\n",
    "    \n",
    "    def draw_body(self):\n",
    "        print(\"画身子——瘦小人\")\n",
    "    \n",
    "    def draw_left_arm(self):\n",
    "        print(\"画左手——瘦小人\")\n",
    "        \n",
    "    def draw_right_arm(self):\n",
    "        print(\"画右手——瘦小人\")\n",
    "        \n",
    "    def draw_left_leg(self):\n",
    "        print(\"画左腿——瘦小人\")\n",
    "    \n",
    "    def draw_rihgt_leg(self):\n",
    "        print(\"画右腿——瘦小人\")\n",
    "        \n",
    "\n",
    "class FatPersonBuilder(PersonBuilder):\n",
    "    \"\"\"\n",
    "    具体建造者，实现创建各个部件的接口\n",
    "    \"\"\"\n",
    "    def draw_head(self):\n",
    "        print(\"画头——胖小人\")\n",
    "    \n",
    "    def draw_body(self):\n",
    "        print(\"画身子——胖小人\")\n",
    "    \n",
    "    def draw_left_arm(self):\n",
    "        print(\"画左手——胖小人\")\n",
    "        \n",
    "    def draw_right_arm(self):\n",
    "        print(\"画右手——胖小人\")\n",
    "        \n",
    "    def draw_left_leg(self):\n",
    "        print(\"画左腿——胖小人\")\n",
    "    \n",
    "    def draw_rihgt_leg(self):\n",
    "        print(\"画右腿——胖小人\")\n",
    "        \n",
    "        \n",
    "class PersonDirector():\n",
    "    \"\"\"\n",
    "    指挥者，是构建一个使用Builder接口的对象\n",
    "    \"\"\"\n",
    "    def __init__(self, person_builder):\n",
    "        self.person_builder = person_builder\n",
    "        \n",
    "    def create_person(self):\n",
    "        self.person_builder.draw_head()\n",
    "        self.person_builder.draw_body()\n",
    "        self.person_builder.draw_left_arm()\n",
    "        self.person_builder.draw_right_arm()\n",
    "        self.person_builder.draw_left_leg()\n",
    "        self.person_builder.draw_right_leg()\n",
    "        \n",
    "def main():\n",
    "    \n",
    "    thin_person_builder = ThinPersonBuilder()\n",
    "    thin_person_director = PersonDirector(thin_person_builder)\n",
    "    thin_person_director.create_person()\n",
    "    \n",
    "    fat_person_builder = FatPersonBuilder()\n",
    "    fat_person_director = PersonDirector(fat_person_builder)\n",
    "    fat_person_director.create_person()\n",
    "    \n",
    "main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 点评\n",
    "建造者模式的好处就是使得建造代码与表示代码分离，由于建造者隐藏了该产品是如何组装的，所以需要改变一个产品的内部表示，只需要再定义一个具体的建造者即可。\n",
    "\n",
    "建造者模式是在当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时适用的模式。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
